/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.huichenghe.xinlvsh01.Ble;

import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;

import com.huichenghe.xinlvsh01.Utils.FormatUtils;


import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;

/**
 * 用于管理连接服务和数据托管在一个给定的蓝牙LE设备GATT服务器通信。
 * Service for managing connection and data communication with a GATT server
 * hosted on a given Bluetooth LE device.
 */
public class BluetoothLeService extends Service{
    private final static String TAG = BluetoothLeService.class.getSimpleName();
    public static final long DEFAULT_DELAY_TIME = 120;

    private static final boolean NeedAutoPair = false;
    private static final boolean NeedReadRssi = false;
    BoundReceiver mReceiver;
    
    
    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothGatt mBluetoothGatt;
    private boolean mOptConnected = true;
    private List<IServiceCallback> mServiceCallbacks = new ArrayList<IServiceCallback>();
    
    private Vector<BleWriteOperate> mPendingWriteOperates = new Vector<BleWriteOperate>();
    private Object mPendingLock = new Object();
    
    public static void printCharacteristicProperty(BluetoothGattCharacteristic characteristic){
        if(characteristic == null){
            Log.e("", "checkCharacteristicProperty fail");
            return;
        }
        int property = characteristic.getProperties();
        
        Log.i("", "PROPERTY_BROADCAST         :"+ ((BluetoothGattCharacteristic.PROPERTY_BROADCAST & property) != 0));
        Log.i("", "PROPERTY_EXTENDED_PROPS    :"+ ((BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS & property) != 0));
        Log.i("", "PROPERTY_INDICATE          :"+ ((BluetoothGattCharacteristic.PROPERTY_INDICATE & property) != 0));
        Log.i("", "PROPERTY_NOTIFY            :"+ ((BluetoothGattCharacteristic.PROPERTY_NOTIFY & property) != 0));
        Log.i("", "PROPERTY_READ              :"+ ((BluetoothGattCharacteristic.PROPERTY_READ & property) != 0));
        Log.i("", "PROPERTY_SIGNED_WRITE      :"+ ((BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE & property) != 0));
        Log.i("", "PROPERTY_WRITE             :"+ ((BluetoothGattCharacteristic.PROPERTY_WRITE & property) != 0));
        Log.i("", "PROPERTY_WRITE_NO_RESPONSE :"+ ((BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE & property) != 0));
    }
    
    
    public synchronized void writeCharacteristic(final BluetoothGattCharacteristic characteristic, final WriteCallBack callback)
    {
    	if(isRunOnUIThread()){
    		if(mBluetoothGatt != null){
    			if(characteristic != null){
    				boolean result2 = mBluetoothGatt.writeCharacteristic(characteristic);
    				if(callback != null){
    					callback.onWrite(true);
    				}
    			}else{
    				Log.e("", "characteristic is null");
    				if(callback != null){
    					callback.onWrite(false);
    				}
    			}
    		}else{
    			Log.e("", "mBluetoothGatt is null");
    			if(callback != null){
    				callback.onWrite(false);
    			}
    		}
    		
    	}else{
    		mHandler.post(new Runnable() {
    			
    			@Override
    			public void run() {
    				writeCharacteristic(characteristic, callback);
    			}
    		});
    	}
    }
    
    public synchronized void writePendingValue(final byte[] value, final BluetoothGattCharacteristic characteristic)
    {
    	if(isRunOnUIThread()){
    		if(mBluetoothGatt != null){
                if(characteristic != null){
                    synchronized (mPendingLock) {
                        
                        if(mPendingWriteOperates.isEmpty()){
                            BleWriteByteArrayOperate operate = new BleWriteByteArrayOperate(value, characteristic);
                            mPendingWriteOperates.add(operate);
                            characteristic.setValue(value);
                            operate.setmOperateState(OperateState.RUNNING);
                            Log.w("", String.format("writePendingValue[%d] +++dowrite, by user empty pengdinglist", mPendingWriteOperates.size()));
                            mBluetoothGatt.writeCharacteristic(characteristic);
//                            Log.e(String.format("writePendingValue[%d] +++fail", mPendingWriteOperates.size()));
//                            operate.setmOperateState(OperateState.RUNNED_FAIL);
//                            mPendingWriteOperates.remove(operate);
                        }else{
                            int length = mPendingWriteOperates.size();
                            boolean haveRunningOpeate = false;
                            for (int i = 0; i < length; i++) {
                            	BleWriteOperate o = mPendingWriteOperates.get(i);
                                if(o.getmOperateState() == OperateState.RUNNING){
                                    haveRunningOpeate = true;
                                    break;
                                }
                            }
                            BleWriteByteArrayOperate now = new BleWriteByteArrayOperate(value, characteristic);
                            mPendingWriteOperates.add(now);
                            now.setmOperateState(OperateState.PENDING);
                            if(haveRunningOpeate){
                            	Log.i("", String.format("writePendingValue[%d] +++have running operate, add operate to pendinglist", mPendingWriteOperates.size()));
                            }else{
                            	BleWriteOperate operate = mPendingWriteOperates.get(0);
                                if(operate.getmCharacteristic() != null){
                                	if(operate instanceof BleWriteByteArrayOperate){
                                		operate.getmCharacteristic().setValue(((BleWriteByteArrayOperate)operate).getmValue());
                                		Log.w("", String.format("writePendingValue[%d] +++dowrite, by user", mPendingWriteOperates.size()));
                                		operate.setmOperateState(OperateState.RUNNING);
                                		mBluetoothGatt.writeCharacteristic(operate.getmCharacteristic());
                                	}else if(operate instanceof BleWriteTypeOperate){
                                		BleWriteTypeOperate writeTypeOpe = (BleWriteTypeOperate)operate;
                                		operate.getmCharacteristic().setValue(writeTypeOpe.getmValue(), writeTypeOpe.getmFormatType(), writeTypeOpe.getmOffset());
                                		Log.w("", String.format("writePendingValue[%d] +++dowrite, by user", mPendingWriteOperates.size()));
                                		operate.setmOperateState(OperateState.RUNNING);
                                		mBluetoothGatt.writeCharacteristic(operate.getmCharacteristic());
                                		
                                	}
//                                    if(result & result2){
//                                    	
//                                    }else{
//                                        Log.e("writePendingValue +++fail2");
//                                        operate.setmOperateState(OperateState.RUNNED_FAIL);
//                                        mPendingWriteOperates.remove(operate);
//                                    }
                                }else{
                                    Log.e("", "writePendingValue +++fail3");
                                    operate.setmOperateState(OperateState.RUNNED_FAIL);
                                    mPendingWriteOperates.remove(operate);
                                }
                            }
                        }
                    }
                }else{
                    Log.e("", "characteristic is null");
                }
            }else{
                Log.e("", "mBluetoothGatt is null");
            }
    	}else{
    		mHandler.post(new Runnable() {
				
				@Override
				public void run() {
					writePendingValue(value, characteristic);
				}
			});
    	}
        
    }
    
    public synchronized void writePendingValue
            (final int value, final int formatType, final int offset, final BluetoothGattCharacteristic characteristic)
    {
    	if(isRunOnUIThread()){
    		if(mBluetoothGatt != null){
                if(characteristic != null){
                    synchronized (mPendingLock) {
                        
                        if(mPendingWriteOperates.isEmpty()){
                            BleWriteTypeOperate operate = new BleWriteTypeOperate(value,formatType, offset, characteristic);
                            mPendingWriteOperates.add(operate);
                            characteristic.setValue(value, formatType, offset);
                            operate.setmOperateState(OperateState.RUNNING);
                            Log.w("", String.format("writePendingValue[%d] +++dowrite, by user empty pengdinglist", mPendingWriteOperates.size()));
                            mBluetoothGatt.writeCharacteristic(characteristic);
//                            Log.e(String.format("writePendingValue[%d] +++fail", mPendingWriteOperates.size()));
//                            operate.setmOperateState(OperateState.RUNNED_FAIL);
//                            mPendingWriteOperates.remove(operate);
                        }else{
                            int length = mPendingWriteOperates.size();
                            boolean haveRunningOpeate = false;
                            for (int i = 0; i < length; i++) {
                            	BleWriteOperate o = mPendingWriteOperates.get(i);
                                if(o.getmOperateState() == OperateState.RUNNING){
                                    haveRunningOpeate = true;
                                    break;
                                }
                            }
                            BleWriteTypeOperate now = new BleWriteTypeOperate(value,formatType, offset, characteristic);
                            mPendingWriteOperates.add(now);
                            now.setmOperateState(OperateState.PENDING);
                            if(haveRunningOpeate){
                            	Log.i("", String.format("writePendingValue[%d] +++have running operate, add operate to pendinglist", mPendingWriteOperates.size()));
                            }else{
                            	BleWriteOperate operate = mPendingWriteOperates.get(0);
                                if(operate.getmCharacteristic() != null){
                                	if(operate instanceof BleWriteByteArrayOperate){
                                		operate.getmCharacteristic().setValue(((BleWriteByteArrayOperate)operate).getmValue());
                                		Log.w("", String.format("writePendingValue[%d] +++dowrite, by user", mPendingWriteOperates.size()));
                                		operate.setmOperateState(OperateState.RUNNING);
                                		mBluetoothGatt.writeCharacteristic(operate.getmCharacteristic());
                                	}else if(operate instanceof BleWriteTypeOperate){
                                		BleWriteTypeOperate writeTypeOpe = (BleWriteTypeOperate)operate;
                                		operate.getmCharacteristic().setValue(writeTypeOpe.getmValue(), writeTypeOpe.getmFormatType(), writeTypeOpe.getmOffset());
                                		Log.w("", String.format("writePendingValue[%d] +++dowrite, by user", mPendingWriteOperates.size()));
                                		operate.setmOperateState(OperateState.RUNNING);
                                		mBluetoothGatt.writeCharacteristic(operate.getmCharacteristic());
                                		
                                	}
//                                    if(result & result2){
//                                    	
//                                    }else{
//                                        Log.e("writePendingValue +++fail2");
//                                        operate.setmOperateState(OperateState.RUNNED_FAIL);
//                                        mPendingWriteOperates.remove(operate);
//                                    }
                                }else{
                                    Log.e("", "writePendingValue +++fail3");
                                    operate.setmOperateState(OperateState.RUNNED_FAIL);
                                    mPendingWriteOperates.remove(operate);
                                }
                            }
                        }
                    }
                }else{
                    Log.e("", "characteristic is null");
                }
            }else{
                Log.e("", "mBluetoothGatt is null");
            }
    	}else{
    		mHandler.post(new Runnable() {
				
				@Override
				public void run() {
					writePendingValue(value, formatType, offset, characteristic);
				}
			});
    	}
        
    }
    
    public interface WriteCallBack{
        void onWrite(boolean result);
    }
    
    public void writeDelayValue(final byte[] value, final BluetoothGattCharacteristic characteristic, final WriteCallBack callback)
    {
//        if(isRunOnUIThread()) {
            if(mBluetoothGatt != null){
                if(characteristic != null){
                    characteristic.setValue(value);
                    mBluetoothGatt.writeCharacteristic(characteristic);
                    Log.i("", "writeDelayValue setValue done:"+ FormatUtils.bytesToHexString(value));
                    if(callback != null){
                        callback.onWrite(true);
                    }
                }else{
                    Log.e("", "writeDelayValue() characteristic is null");
                    if(callback != null){
                        callback.onWrite(false);
                    }
                }
            }else{
                Log.e("", "writeDelayValue() setValue fail:"+FormatUtils.bytesToHexString(value));
                if(callback != null){
                    callback.onWrite(false);
                }
            }
            
//        }else{
//        	Log.i("writeDelayValue()---begin---2");
//            mHandler.post(new Runnable() {
//                
//                @Override
//                public void run() {
//                    writeDelayValue(value, characteristic, callback);
//                }
//            });
//        }
    }
    
    public void writeDelayValue(final int value, final int formatType, final int offset, final BluetoothGattCharacteristic characteristic, final WriteCallBack callback)
    {
//        if(isRunOnUIThread()){
            if(mBluetoothGatt != null){
                if(characteristic != null){
                    boolean result = characteristic.setValue(value, formatType, offset);
                    boolean result2 = mBluetoothGatt.writeCharacteristic(characteristic);
                    Log.i("", "writeDelayValue2 setValue done value:" + value);
                    if(callback != null){
                        callback.onWrite(true);
                    }
                }else{
                    Log.e("", "characteristic is null");
                    if(callback != null){
                        callback.onWrite(false);
                    }
                }
            }else{
                Log.e("", "writeDelayValue2 setValue fail value:" + value);
                if(callback != null){
                    callback.onWrite(false);
                }
            }
            
//        }else{
//            mHandler.post(new Runnable() {
//                
//                @Override
//                public void run() {
//                    writeDelayValue(value, formatType, offset, characteristic, callback);
//                }
//            });
//        }
    }
    
    public void WriteValue(final BluetoothGattCharacteristic characteristic, final WriteCallBack callback)
    {
    	if(isRunOnUIThread()){
    		if(mBluetoothGatt != null){
    			if(characteristic != null){
    				boolean result2 = mBluetoothGatt.writeCharacteristic(characteristic);
    				if(callback != null){
    					callback.onWrite(true);
    				}
    			}else{
    				Log.e("", "characteristic is null");
    				if(callback != null){
    					callback.onWrite(false);
    				}
    			}
    		}else{
    			Log.e("", "mBluetoothGatt is null");
    			if(callback != null){
    				callback.onWrite(false);
    			}
    		}
    		
    	}else{
    		mHandler.post(new Runnable() {
    			
    			@Override
    			public void run() {
    				WriteValue(characteristic, callback);
    			}
    		});
    	}
    }
    
    public boolean isConnectedDevice(){
        if(mBluetoothGatt == null){
            return false;
        }else{
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(mBluetoothGatt);
            if(device == null){
                return false;
            }else{
                return isDeviceConnected(device);
            }
        }
        
    }
    
    public List<BluetoothDevice> getConnectedDevices(){
        if(mBluetoothManager == null){
            return null;
        }else{
            return mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER);
        }
    }
    
    public boolean isDeviceConnected(LocalDeviceEntity device){
        List<BluetoothDevice> devices = getConnectedDevices();
        if(device == null || devices == null || devices.isEmpty()){
            return false;
        }
        int size = devices.size();
        for (int i = 0; i < size; i++) {
            BluetoothDevice bluetoothDevice = devices.get(i);
            if(device.getAddress().equals(bluetoothDevice.getAddress())){
                return true;
            }
        }
        return false;
    }
    
    private boolean isConnectingDevice = false;
    public boolean isConnectingDevice(){
        return isConnectingDevice;
    }
    
    private boolean isDiscoveringService = false;
    
    public boolean isDiscoveringService(){
        return isDiscoveringService;
    }
    
    private synchronized void readDeviceRssi(){
        mHandler.postDelayed(new Runnable() {
            
            @Override
            public void run() {
                if(isServiceRunning && isConnectedDevice()){
                	Log.d("", "readDeviceRssi");
                    mBluetoothGatt.readRemoteRssi();
                    readDeviceRssi();
                }else{
                    Log.e("", "readDeviceRssi fail, service stop");
                }
            }
        }, 1000);
    }
    
    public synchronized void readDevicePower(final UUID serviceuuid, final UUID batteryuuid){
        if(isRunOnUIThread()){
            if(isServiceRunning && isConnectedDevice()){
                BluetoothGattService batteryservice = mBluetoothGatt.getService(serviceuuid);
                if(batteryservice != null){
                    BluetoothGattCharacteristic characteristic = batteryservice.getCharacteristic(batteryuuid);
                    if(characteristic != null){
                        Log.i("", "<<start read device power>>");
                        readCharacteristic(characteristic);
                    }else{
                        Log.e("", "readDevicePower fail, characteristic null");
                    }
                }else{
                    Log.e("", "readDevicePower fail, battery service null");
                }
            }else{
                Log.e("", "readDevicePower fail, mBluetoothGatt null");
            }
            
        }else{
            mHandler.post(new Runnable() {
                
                @Override
                public void run() {
                    readDevicePower(serviceuuid, batteryuuid);
                }
            });
        }
    }
    
    // 回调接口，建立GATT连接后，回传所需要的数据，即动作
    // Implements callback methods for GATT events that the app cares about. For
    // example,
    // connection change and services discovered.
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
    {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
        {
        	boolean success = (status == BluetoothGatt.GATT_SUCCESS);
            Log.w("", "onConnectionStateChange success=" + success + " NewStates=" + newState);
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            if (status == BluetoothGatt.GATT_SUCCESS)
            {

                if (newState == BluetoothProfile.STATE_CONNECTED)
                {
                    Log.w("", "Connected to GATT server.");
                    isConnectingDevice = false;
                    List<BluetoothDevice> devicesList = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER);
                    if(devicesList != null && !devicesList.isEmpty())
                    {
                        for (int i = 0; i < devicesList.size(); i++)
                        {
                            Log.i("", "@@11connected device:"+devicesList.get(i).getName());
                        }
                    }
                    if (!mServiceCallbacks.isEmpty())
                    {
                        int size = mServiceCallbacks.size();
                        for (int i = 0; i < size; i++)
                        {
                            IServiceCallback callback = mServiceCallbacks.get(i);
                            callback.onBLEDeviceConnected(device, gatt);
                        }
                    }
                    if(NeedReadRssi){
                    	readDeviceRssi();
                    }
                    // Attempts to discover services after successful
                    // connection.
                    boolean result = gatt.discoverServices();//bluetoothAdapter.cancelDiscovery();可能会对此方法有影响,导致找不到服务
                    isDiscoveringService = result;//true
                    Log.w("", "Attempting to start service discovery:" + result);
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
//                    if(gatt.connect()){//是否可以做为可连接设备？？？
//                    Log.w("Disconnected from GATT server. reconnect it");
                        //TODO 参照BLEEventBus
//                        List<BluetoothDevice> devicesList = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER);
//                        if(devicesList != null && !devicesList.isEmpty()){
//                            for (int i = 0; i < devicesList.size(); i++) {
//                                Log.i("after reconnect @@22connected device:"+devicesList.get(i).getName());
//                            }
//                        }
//                        
//                    }else{
//                        
//                        if(gatt != null){
//                            Engine.getInstance().removeGattFromDeviceMap(gatt);
//                            gatt.disconnect();
//                            gatt.close();
//                            gatt = null;
//                        }
//                    Log.w("Disconnected from GATT server.");
                        isConnectingDevice = true;
                        isDiscoveringService = false;
                        
                        if (mOptConnected) {
	                        if (!mServiceCallbacks.isEmpty()) {
	                            int size = mServiceCallbacks.size();
	                            for (int i = 0; i < size; i++) {
	                                IServiceCallback callback = mServiceCallbacks.get(i);
	                                callback.onBLEDeviceDisConnected(device, gatt);
	                            }
	                        }
	                        
	                        Log.w("", "Disconnected from GATT server. reconnect it, mOptConnected="+mOptConnected);
	                        connect(device);
                        }
//                    }
                }else if(newState == BluetoothProfile.STATE_CONNECTING){
                    isConnectingDevice = true;
                    Log.w("", "connecting to device");
                }
            }else{
                Log.w("", "connect device error");
                isConnectingDevice = false;
                isDiscoveringService = false;
                if(gatt != null){
                    synchronized (Engine.getInstance()) {
                        Engine.getInstance().removeGattFromDeviceMap(gatt);
                        mPendingWriteOperates.clear();
                        gatt.disconnect();
                        gatt.close();
                        gatt = null;
                    }
                }
                if (!mServiceCallbacks.isEmpty()) {
                    int size = mServiceCallbacks.size();
                    for (int i = 0; i < size; i++) {
                        IServiceCallback callback = mServiceCallbacks.get(i);
                        callback.onBLEDeviceConnecError(device,				//设备 
                        								true,				//是否显示toast
                        								true);				//是否是主动断开
                    }
                }
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            isDiscoveringService = false;
            boolean success = (status == BluetoothGatt.GATT_SUCCESS);
            Log.i("", "onServicesDiscovered success: " + success);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if(gatt.getServices() != null ){
                    if (!mServiceCallbacks.isEmpty()) {
                        int size = mServiceCallbacks.size();
                        for (int i = 0; i < size; i++) {
                            IServiceCallback callback = mServiceCallbacks.get(i);
                            callback.onBLEServiceFound(device, gatt, gatt.getServices());
                        }
                    }
                }else{
                    if (!mServiceCallbacks.isEmpty()) {
                        int size = mServiceCallbacks.size();
                        for (int i = 0; i < size; i++) {
                            IServiceCallback callback = mServiceCallbacks.get(i);
                            callback.onNoBLEServiceFound();
                        }
                    } 
                }
            } else {
                if (!mServiceCallbacks.isEmpty()) {
                    int size = mServiceCallbacks.size();
                    for (int i = 0; i < size; i++) {
                        IServiceCallback callback = mServiceCallbacks.get(i);
                        callback.onNoBLEServiceFound();
                    }
                }
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic,
                int status) {
        	boolean success = (status == BluetoothGatt.GATT_SUCCESS);
        	byte[] value = characteristic.getValue();
            Log.i("", "onCharacteristicRead success: " + success + " value:" + FormatUtils.bytesToHexString(value));
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            if (!mServiceCallbacks.isEmpty()) {
            	int size = mServiceCallbacks.size();
            	for (int i = 0; i < size; i++) {
            		IServiceCallback callback = mServiceCallbacks.get(i);
            		callback.onCharacteristicRead(device, gatt, characteristic, success);
            	}
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic) {
        	final byte[] value = characteristic.getValue();
        	final String uuid = characteristic.getUuid().toString();
            Log.i("", "onCharacteristicChanged receive the notify value:" + FormatUtils.bytesToHexString(value));
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            if (!mServiceCallbacks.isEmpty()) {
                int size = mServiceCallbacks.size();
                for (int i = 0; i < size; i++) {
                    IServiceCallback callback = mServiceCallbacks.get(i);
                    callback.onCharacteristicChanged(device, gatt, uuid, value);
                }
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic,
                int status)
        {
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            
            synchronized (mPendingLock) {
                if(!mPendingWriteOperates.isEmpty()){
	                    int length = mPendingWriteOperates.size();
//	                    boolean haveRunningOpeate = false;
//	                    int index = -1;
	                    for (int i = 0; i < length; i++) {
	                        BleWriteOperate o = mPendingWriteOperates.get(i);
	                        if(o.getmOperateState() == OperateState.RUNNING){
//	                            haveRunningOpeate = true;
//	                            index = i;
	                            mPendingWriteOperates.remove(o);
	                            Log.w("", String.format("onCharacteristicWrite[%d] done", mPendingWriteOperates.size()));
	                            break;
	                        }
	                    }
//	                    if(haveRunningOpeate && index != -1){
//	                        BleWriteOperate operate = mPendingWriteOperates.get(index);
//	                        operate.setmOperateState(status == BluetoothGatt.GATT_SUCCESS ? OperateState.RUNNED_SUCCESS : OperateState.RUNNED_FAIL);
//	                    }
                    
                }else{
                    Log.e("", "onCharacteristicWrite +++mPendingWriteOperates is empry");
                }
            }
            
            byte[] value = characteristic.getValue();
            boolean success = (status == BluetoothGatt.GATT_SUCCESS);
            Log.w("", "onCharacteristicWrite success: " + success + " value:" + FormatUtils.bytesToHexString(value));
            if (!mServiceCallbacks.isEmpty()) {
            	int size = mServiceCallbacks.size();
            	
            	for (int i = 0; i < size; i++) {
            		IServiceCallback callback = mServiceCallbacks.get(i);
            		callback.onCharacteristicWrite(device, gatt, characteristic, success);
            	}
            }
            
            autoRunNext();
            
        }

        private void autoRunNext() {
        	if(!mPendingWriteOperates.isEmpty()){
                final BleWriteOperate operate = mPendingWriteOperates.get(0);
                Log.d("", "autoRunNext operate:" + operate.getmOperateState());
                if(operate.isPending()){
                	Log.i("", String.format("autoRunNext[%d] auto run next", mPendingWriteOperates.size()));
                	mHandler.post(new Runnable() {
                		
                		@Override
                		public void run() {
                			synchronized (mPendingLock) {
                				if(operate.getmCharacteristic() != null){
                					if(operate instanceof BleWriteByteArrayOperate){
                						operate.getmCharacteristic().setValue(((BleWriteByteArrayOperate)operate).getmValue());
                                		Log.w("", String.format("autoRunNext[%d] +++dowrite, auto", mPendingWriteOperates.size()));
                                		operate.setmOperateState(OperateState.RUNNING);
                                		mBluetoothGatt.writeCharacteristic(operate.getmCharacteristic());
                                	}else if(operate instanceof BleWriteTypeOperate){
                                		BleWriteTypeOperate writeTypeOpe = (BleWriteTypeOperate)operate;
                                		operate.getmCharacteristic().setValue(writeTypeOpe.getmValue(), writeTypeOpe.getmFormatType(), writeTypeOpe.getmOffset());
                                		Log.w("", String.format("autoRunNext[%d] +++dowrite, auto", mPendingWriteOperates.size()));
                                		operate.setmOperateState(OperateState.RUNNING);
                                		mBluetoothGatt.writeCharacteristic(operate.getmCharacteristic());
                                		
                                	}
//                					Log.w(String.format("autoRunNext[%d] +++dowrite, auto", mPendingWriteOperates.size()));
//                					boolean result2 = mBluetoothGatt.writeCharacteristic(operate.getmCharacteristic());
//                					if(result & result2){
//                						
//                					}else{
//                						Log.i(String.format("autoRunNext[%d] +++fail2", mPendingWriteOperates.size()));
//                						operate.setmOperateState(OperateState.RUNNED_FAIL);
//                						mPendingWriteOperates.remove(operate);
//                					}
                				}else{
                					Log.i("", String.format("autoRunNext[%d] +++fail3", mPendingWriteOperates.size()));
                					operate.setmOperateState(OperateState.RUNNED_FAIL);
                					mPendingWriteOperates.remove(operate);
                				}
                			}
                		}
                	});
                }else if(operate.isRunning()){
                	Log.i("", String.format("autoRunNext[%d] +++but next opearte is running", mPendingWriteOperates.size()));
                }else if(operate.hasHandled()){
                	Log.i("", String.format("autoRunNext[%d] +++but next opearte have handled", mPendingWriteOperates.size()));
                	mPendingWriteOperates.remove(operate);
                	autoRunNext();
                }else{
                	Log.e("", String.format("autoRunNext[%d] +++ignore this one auto run next", mPendingWriteOperates.size()));
                	mPendingWriteOperates.remove(operate);
                	autoRunNext();
                }
            }else{
            	Log.e("", String.format("autoRunNext[%d] +++pending list empty, no next", mPendingWriteOperates.size()));
            }
			
		}

		@Override
        public void onDescriptorRead(BluetoothGatt gatt,
                BluetoothGattDescriptor bd,
                int status) {
			boolean success = (status == BluetoothGatt.GATT_SUCCESS);
            Log.w("", "onDescriptorRead success: " + success);
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            if (!mServiceCallbacks.isEmpty()) {
            	int size = mServiceCallbacks.size();
            	for (int i = 0; i < size; i++) {
            		IServiceCallback callback = mServiceCallbacks.get(i);
            		callback.onDescriptorRead(device, gatt, bd, success);
            	}
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt,
                BluetoothGattDescriptor bd,
                int status) {
        	boolean success = (status == BluetoothGatt.GATT_SUCCESS);
            Log.w("", "onDescriptorWrite success: " + success);
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            if (!mServiceCallbacks.isEmpty()) {
            	int size = mServiceCallbacks.size();
            	for (int i = 0; i < size; i++) {
            		IServiceCallback callback = mServiceCallbacks.get(i);
            		callback.onDescriptorWrite(device, gatt, bd, success);
            	}
            }
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
        {
        	boolean success = (status == BluetoothGatt.GATT_SUCCESS);
            Log.d("", "onReadRemoteRssi success: " + success);
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            if (!mServiceCallbacks.isEmpty()) {
            	int size = mServiceCallbacks.size();
            	for (int i = 0; i < size; i++) {
            		IServiceCallback callback = mServiceCallbacks.get(i);
            		callback.onReadRemoteRssi(device, gatt, rssi, success);
            	}
            }
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status)
        {
        	boolean success = (status == BluetoothGatt.GATT_SUCCESS);
            Log.w("", "onReliableWriteCompleted success: " + success);
            LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(gatt);
            if (!mServiceCallbacks.isEmpty()) {
            	int size = mServiceCallbacks.size();
            	for (int i = 0; i < size; i++) {
            		IServiceCallback callback = mServiceCallbacks.get(i);
            		callback.onReliableWriteCompleted(device, gatt, success);
            	}
            }
        }

    };

    public void addCallback(IServiceCallback callback)
    {
        if(!mServiceCallbacks.contains(callback)){
            mServiceCallbacks.add(callback);
        }
    }
    
    public void removeCallback(IServiceCallback callback)
    {
        if(mServiceCallbacks.contains(callback)){
            mServiceCallbacks.remove(callback);
        }
    }

    public class LocalBinder extends Binder
    {
        public BluetoothLeService getService() {
            return BluetoothLeService.this;
        }
    }

    /**
     * Initializes a reference to the local Bluetooth adapter.
     * 
     * @return Return true if the initialization is successful.
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter
        // through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e("", "Unable to initialize BluetoothManager.");
                return false;
            }
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            Log.e("", "Unable to obtain a BluetoothAdapter.");
            return false;
        }
        isConnectingDevice = false;
        isDiscoveringService = false;
        return true;
    }
    

    /**
     * Connects to the GATT server hosted on the Bluetooth LE device.
     * @param device    the destination device
     * @return  Return true if the connection is initiated successfully. The
     *         connection result is reported asynchronously through the
     *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     *         callback.
     */
    public synchronized boolean connect(LocalDeviceEntity device) {
//    	Exception debug = new Exception();
//    	debug.printStackTrace();
    	mOptConnected = true;
    	
        if (mBluetoothAdapter == null || device == null || TextUtils.isEmpty(device.getAddress())) {
            Log.w("", "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }
        /*
         * // Previously connected device. Try to reconnect. if
         * (mBluetoothDeviceAddress != null &&
         * address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
         * Log.d(TAG,
         * "Trying to use an existing mBluetoothGatt for connection."); if
         * (mBluetoothGatt.connect()) { mConnectionState = STATE_CONNECTING;
         * return true; } else { return false; } }
         */
        BluetoothDevice d = mBluetoothAdapter.getRemoteDevice(device.getAddress());
        if (device == null) {
            Log.w("", "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the
        // autoConnect
        // parameter to false.
        if (mBluetoothGatt != null)
        {
            Log.w("", "close last BluetoothGatt");
            synchronized (Engine.getInstance()) {
                Engine.getInstance().removeGattFromDeviceMap(mBluetoothGatt);
                mPendingWriteOperates.clear();
                mBluetoothGatt.close();
                mBluetoothGatt = null;
            }
        }
        if(NeedAutoPair){
            boolean result = BluetoothAutoPair.pair(d, "1234");
            Log.w("", "auto pair:" + result);
        }
        boolean hasBouded = (d.getBondState() == BluetoothDevice.BOND_BONDED);
        Log.i("", " +++++connect+++++" + device.getName() + " have bond:" + hasBouded);
        isConnectingDevice = true;
        isDiscoveringService = false;
        if(!hasBouded && NeedAutoPair){
            Log.w("", "device have not bound,wait for bounded");
            if(mReceiver != null){
                mReceiver.setTargetDevice(device);
            }
        }else{
            if(mReceiver != null){
                mReceiver.setTargetDevice(device);
            }
            synchronized (Engine.getInstance()) {
                mBluetoothGatt = d.connectGatt(this, false, mGattCallback);
                Engine.getInstance().addGatt2DeviceMap(mBluetoothGatt, device);
            }
        }
        Log.i("", "-----connect-----" + device.getName());
        // mBluetoothGatt.connect();

        Log.d("", "Trying to create a new connection.");
        return true;
    }

    /**
     * Disconnects an existing connection or cancel a pending connection. The
     * disconnection result is reported asynchronously through the
     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     * callback.
     */
    public synchronized void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w("", "disconnect fail, BluetoothAdapter not initialized");
            return;
        }
        Log.i("", "disconnect an existing connection or cancel a pending connection  mOptConnected = false");
        mOptConnected = false;
        mBluetoothGatt.disconnect();
    }

    /**
     * After using a given BLE device, the app must call this method to ensure
     * resources are released properly.
     */
    public synchronized void close(boolean showToast) {
        if (mBluetoothGatt == null) {
            return;
        }
        Log.w("", "close device");
        isConnectingDevice = false;
        isDiscoveringService = false;
        LocalDeviceEntity device = Engine.getInstance().getDeviceFromGatt(mBluetoothGatt);
        synchronized (Engine.getInstance()) {
            Log.i("", "close mBluetoothGatt");
            Engine.getInstance().removeGattFromDeviceMap(mBluetoothGatt);
            mPendingWriteOperates.clear();
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
        if (!mServiceCallbacks.isEmpty()) {
            int size = mServiceCallbacks.size();
            for (int i = 0; i < size; i++) {
                IServiceCallback callback = mServiceCallbacks.get(i);
                callback.onBLEDeviceConnecError(device, 
                								showToast,// 显示提示信息
                								false);   // 主动断开
            }
        }
    }

    /**
     * Request a read on a given {@code BluetoothGattCharacteristic}. The read
     * result is reported asynchronously through the
     * {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
     * callback.
     * 
     * @param characteristic The characteristic to read from.
     */
    public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w("", "BluetoothAdapter not initialized");
            return false;
        }
        return mBluetoothGatt.readCharacteristic(characteristic);
    }

    /**
     * Enables or disables notification on a give characteristic.
     * 
     * @param characteristic Characteristic to act on.
     * @param enabled If true, enable notification. False otherwise.
     */
    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
            boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w("", "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
        if(descriptors != null && !descriptors.isEmpty()){
            for (int i = 0; i < descriptors.size(); i++) {
                BluetoothGattDescriptor lastDescriptor = descriptors.get(i);
                if(enabled){
                	lastDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                }else{
                	lastDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
                }
                mBluetoothGatt.writeDescriptor(lastDescriptor);
            }
        }
        /*
         * // This is specific to Heart Rate Measurement. if
         * (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
         * BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
         * UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
         * descriptor
         * .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
         * mBluetoothGatt.writeDescriptor(descriptor); }
         */
    }

    /**
     * Retrieves a list of supported GATT services on the connected device. This
     * should be invoked only after {@code BluetoothGatt#discoverServices()}
     * completes successfully.
     * 
     * @return A {@code List} of supported services.
     */
    public List<BluetoothGattService> getSupportedGattServices() {
        if (mBluetoothGatt == null)
            return null;

        return mBluetoothGatt.getServices();
    }
    
    public BluetoothGatt getBluetoothGatt() {
		return mBluetoothGatt;
	}

	//--------------------------------------SERVICE--------------------------------------------------
    boolean isServiceRunning;
    private static BluetoothLeService sInBluetoothLeService;
    private long mUIThreadId;
    
    boolean isRunOnUIThread() {
        return mUIThreadId == Thread.currentThread().getId();
    }

    public synchronized static BluetoothLeService getInstance() {
        return sInBluetoothLeService;
    }

    Handler mHandler;
    @Override
    public void onCreate()
    {
        // TODO Auto-generated method stub
        super.onCreate();
        Log.i("", "<< BLE Service onCreate >>");
        mHandler = new Handler();
        mUIThreadId = Thread.currentThread().getId();
        sInBluetoothLeService = this;
        isServiceRunning = true;
        initialize();
        if(NeedAutoPair){
            mReceiver = new BoundReceiver();
            IntentFilter filter = new IntentFilter();
            filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
            registerReceiver(mReceiver, filter);
        }
    }
    
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        Log.i("", "<< BLE Service onDestory >>");
        mPendingWriteOperates.clear();
        
        if(NeedAutoPair){
            unregisterReceiver(mReceiver);
        }
        sInBluetoothLeService = null;
        isServiceRunning = false;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        if (intent != null) {
            Log.i("", "BLE Service onStartCommand");
            isServiceRunning = true;
        }
        return START_STICKY;
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("", "BLE Service onBind");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("", "BLE Service onUnBind");
        // After using a given device, you should make sure that
        // BluetoothGatt.close() is called
        // such that resources are cleaned up properly. In this particular
        // example, close() is
        // invoked when the UI is disconnected from the Service.
        close(true);
        return super.onUnbind(intent);
    }
    
    public boolean isServiceRunning(){
        return isServiceRunning;
    }
    
    private final IBinder mBinder = new LocalBinder();
    
//    public static void startService(Context context){
//        if(sInBluetoothLeService == null){
//            Intent intent = new Intent(context, BluetoothLeService.class);
//            context.startService(intent);
//        }else{
//            Log.w("service have started");
//        }
//    }
//    
//    public static void stopService(Context context){
//        if(sInBluetoothLeService != null){
//            Intent intent = new Intent(context, BluetoothLeService.class);
//            context.stopService(intent);
//        }else{
//            Log.w("service have stoped");
//        }
//    }
    
    private class BoundReceiver extends BroadcastReceiver {    
        private LocalDeviceEntity mTargetDevice;
        public void setTargetDevice(LocalDeviceEntity device){
            mTargetDevice = device;
        }
        
        public void onReceive(Context context, Intent intent) {    
            String action = intent.getAction();    
            Bundle b = intent.getExtras();    
            Object[] lstName = b.keySet().toArray();    
    
            // 显示所有收到的消息及其细节    
            BluetoothDevice device = null;    
            // 搜索设备时，取得设备的MAC地址    
            if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){    
                device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);    
                switch (device.getBondState()) {    
                case BluetoothDevice.BOND_BONDING:    
                    Log.d("", "正在配对......");
                    break;    
                case BluetoothDevice.BOND_BONDED:    
                    boolean needReConn = (mBluetoothGatt == null && isConnectingDevice && mTargetDevice != null && device != null && mTargetDevice.getAddress().equals(device.getAddress()));
                    Log.i("", "完成配对 needReConn:" + needReConn);
                    if(needReConn){
                        connect(mTargetDevice);
                    }
                    break;    
                case BluetoothDevice.BOND_NONE:    
                    Log.d("", "取消配对");
                default:    
                    break;    
                }    
            } 
        }  
    }

    
}
